from datetime import datetime, timedelta

from django.db import models
from django.utils.http import urlquote
from django.contrib.auth.models import User

from pypsqueak.artifact.models import Artifact
from pypsqueak.versioning.models import VersionedModel

class Page(VersionedModel):
	""" The master page class """

	slug = models.SlugField( default="", editable=False, max_length=25 )
	title = models.CharField( default="New Title", max_length=255 )
	created = models.DateTimeField( default=datetime.now, editable=False )
	updated = models.DateTimeField( editable=False )
	body = models.TextField()
	user = models.ForeignKey(User, editable=False)
	parent = models.ForeignKey('Page', blank=True, null=True)
	order = models.IntegerField( default=1 )
	artifacts = models.ManyToManyField(Artifact, through='PageArtifact')

	def getParentTree(self):
		""" Return a list of pages from the page root to the current page """
		# Todo: Make this a more efficient algorithm.
		p = self
		tree = []
		while not p.parent is None:
			tree.insert(0, p.parent)
			p = p.parent
		tree.append(self)
		return tree


	def getChildren(self):
		if not hasattr(self, "__children__"):
			self.populateRelations()
		return self.__children__


	def populateRelations(self, PageDictionary=None):
		"""
		Recursively populate the children dictionary.

		Called automatically when getChildren() is used, but it's a good idea to
		purposefully call to avoid having a new queryset created for each top-level
		child page of a user.
		"""

		# Ignore population command if children is already set.
		if hasattr(self, "__children__"):
			return True

		# Grab all pages by this user if not passed.
		if None == PageDictionary:
			PageDictionary = Page.objects.filter(user=self.user)

		#Preset children as an empty dict and recursively load.
		self.__children__ = []
		for p in PageDictionary:
			if p.parent_id == self.id:
				self.__children__.append(p)
				p.populateRelations(PageDictionary)

		return True


	def __unicode__(self):
		return self.title


	@staticmethod
	def get_insert_url(user):
		return '/%s/page/insert/' % user.username
	def get_absolute_url(self):
		return '/%s/page/%s/' % (self.user.username, self.slug)
	def get_update_url(self):
		return '/%s/page/%s/update/' % (self.user.username, self.slug)
	def get_delete_url(self):
		return '/%s/page/%s/delete/' % (self.user.username, self.slug)


	def save(self ):
		"""Save a new version each time the master page is saved."""
		# Set updated to now.
		self.updated = datetime.now()

		# Set slug if null OR if it has been less than 24 hours since the page was originally created.
		#	This allows a user to create a page from template, save it, rename it, and have
		#	a reasonable url. If the user is also creating a number of blank pages, it also
		#	keeps them from running ontop of each other.
		if len(self.slug) < 1 or (
			self.created >= datetime.now() + timedelta(days=-1)
			):
			max_len = Page._meta.get_field_by_name('slug')[0].max_length

			# Replace all spaces with underline chars. Looks a lot nicer than %20
			slug = self.title.replace(" ", "_")

			# Shorten title to make sure that urlquote doesn't expand it beyond max_len
			# 	urlquote can turn one character into several.
			while len( urlquote( slug ) ) > max_len:
				slug = slug[:-1]

			# is the slug unique to this user?
			if Page.objects.filter( user=self.user, slug=urlquote(slug) ).count() > 0:
				# Turn Slug into Slug_i for uniqueness.
				i = 0
				while True:
					i = i + 1

					# Make sure slug is short enough to fix with the number added.
					while len( urlquote( slug + "_" + str(i) ) ) > max_len:
						slug = slug[:-1]

					# If unique, then set new slug & end loop.
					if Page.objects.filter(
							user=self.user,
							slug=urlquote(slug) + "_" + str(i)
							).count() < 1:

						slug = slug + "_" + str(i)
						break

			# We now have a unique slug that is short enough to fit the field.
			self.slug = urlquote(slug)


		# Set parent to none if set to self.
		if self.parent_id == self.id:
			self.parent = None

		# Call save event.
		super(Page, self).save(False, False)



	#def delete(self):
	#	""" Custom delete """
	#
	#	self.artifact_set.all().delete()
	#	super(Page, self).delete(self)


	@staticmethod
	def insert_permission(target_user, current_user):
		""" Can the user insert a new record? """
		if current_user.is_anonymous() or target_user.is_anonymous():
			return False
		return target_user.pk == current_user.pk
	def permission(self, user, permission):
		""" Determine if the user has permission """
		if permission=='update' or permission=='delete':
			return (user.pk == self.user.pk)
		elif permission=='select':
			return True
		else:
			raise Exception, 'Invalid permission'


	class Admin:
		pass


	class Meta:
		get_latest_by = "updated"
		ordering = [ "order", "title" ]




class PageArtifact(models.Model):
	""" Instances of artifacts for a particular page """
	page = models.ForeignKey(Page)
	artifact = models.ForeignKey(Artifact)
	order = models.IntegerField( default=1 )

	@staticmethod
	def insert_permission(target_page, current_user):
		""" Does the user have permission on the page? """
		if not isinstance(target_page,Page):
			raise Exception('Target_Page must be a page')
		return target_page.permission(current_user,'update')
	def permission(self, user, permission):
		""" Determine if the user has permission by going to the page """
		return self.page.permission(user,permission)

	# Add delete def so that if no other pages are referring to the artifact,
	#	the artifact is deleted.
	#def delete(self):
	#	self.artifact.delete()
	#	super(self,PageArtifact).delete()

	def __unicode__(self):
		return self.artifact.title

	@staticmethod
	def get_insert_url(page):
		return '%sartifact/insert/' % (page.get_absolute_url())
	def get_absolute_url(self):
		(path, sep, ext) = self.artifact.file.name.rpartition('.')
		if ext == 'pdf':
			return '%sartifact/%s/annotate/' % (self.page.get_absolute_url(), self.pk)
		else:
			return '%sartifact/%s/' % (self.page.get_absolute_url(), self.pk)

	def get_update_url(self):
		return '%sartifact/%s/update/' % (self.page.get_absolute_url(), self.pk)
	def get_delete_url(self):
		return '%sartifact/%s/delete/' % (self.page.get_absolute_url(), self.pk)


	class Admin:
		pass

	class Meta:
		ordering = [ "order" ]




def monkeypatch_method(cls):
	"""
	Add ability to add a decorator to a class for adding additional functionality
	See http://mail.python.org/pipermail/python-dev/2008-January/076194.html
	"""
	def decorator(func):
		setattr(cls, func.__name__,func)
		return func
	return decorator

@monkeypatch_method(User)
def getChildren(self):
	"""
	Add ability for User.getChildren() to pull back child pages

	Makes it much easier to create and navigate user tree to treat user as a page.
	"""

	pages = Page.objects.filter( user__id = self.id ).order_by( 'parent','order','title' )
	children = []
	for p in pages:
		if None == p.parent:
			p.populateRelations(pages)
			children.append(p)

	return children